import pandas as pd
import numpy as np

# series 一维带标签数组，可保存任何类型的数据
# dataframe  二维数据结构，可像二维数组或带有行和列的表格一样保存数据。
# 创建 series
s = pd.Series([1, 2, 5, np.nan, 6, 8])
dates = pd.date_range('20240101', periods=6)
# 创建 dataframe
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('abcd'))
# 每列 数据类型不同
df2 = pd.DataFrame(
    {
        'a': 1,
        'b': pd.Timestamp('20240102'),
        'c': pd.Series(1, index=list(range(4)), dtype='float32'),
        'd': np.array([3] * 4, dtype='int32'),
        'e': pd.Categorical(['test', 'train', 'test', 'train']),
        'f': 'foo'
    }
)

# print(df.head())
# print(df.tail(3))

# print(df.index)
# print(df.columns)

# 排序
# df.sort_index() 按轴排序
# df.sort_values() 按值排序

# print(df)
# print(df.sort_index(axis=1, ascending=False))
# print(df.sort_values(by='b'))

# todo 选择
# 1 [] 获取项
# 单个标签类似列索引 datafrmae 传递标签讲选择一列并生成一个等效与 df.a 的 series -> df['a']
# 行切片 : 不同基础切片 左闭右开
# print(df[:3])
# 2 标签选择 行选择 loc iloc 行索引
# 单行索引
# print(df.loc[dates[0]])
# 切片行索引 类似列索引 并且可以筛选列
# print(df.loc[:dates[2], ['a', 'b']])
# 选择单行单列 返回一个标量值
# print(df.at[dates[0], 'a'])

# 3 根据位置选择

# print(df)
# 整数 根据行获取整行
# print(df.iloc[3])
# 切片 df.iloc[开始行:结束行, 开始列:结束列] 不同loc全闭 iloc 等同 python 内置格式切片左闭右开
# print(df.iloc[3:5, 0:2])
# 整数位取值 row:col
# print(df.iloc[[1, 2, 4], [0, 2]])
# 快速访问标量
# print(df.iloc[1, 1])

# 布尔索引 条件筛选
# 列每行数据过滤
# print(df[df['a'] > 0])
# isin([])

# 设置
# 标签设置值
# df.at[dates[0], 'a'] = 0

# df.iat[1, 1] = 1\

# numpy 数据赋值
# df.loc[:, 'd'] = np.array([1] * len(df))

# 带有 where 设置
# df2 = df.copy()
# df 可以使用 比较运算 赋值 -df 由正转负
# df2[df2 > 0] = -df2
# print(df2)

# 运算
# 统计

# print(df)
# 一般情况运算会排除缺失值
# 每列求平均值
# print(df.mean())
# 计算每行平均值
# print(df.mean(axis=1))

# shift
# shift(period: int, freq, axis） 移动数据
# period 位移步长 可正可负。移动数据不移动索引
# 适用时间序列数据存在，根据period移动时间索引，数据不做变化
# axis 0 垂直 1 水平
# s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2)
# print(s)
# 根据 s 延伸扩散 todo
# print(df.sub(s, axis='index'))

# 用户定义函数
# agg 在指定轴上使用一个或多个操作进行聚合
#
# print(df.agg(lambda x: np.mean(x) * 5.6))
# print(df.transform(lambda x: x * 101))

# 分组


# 两数之和20, 330

# def f(x):
#     print(x['a'])
#
# df.agg(f, axis=1)
# 值计数
# s = pd.Series(np.random.randint(0, 7, size=10))
# print(s)
# 计数
# print(s.value_counts())
# 字符串 方法
# s = pd.Series(["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"])
# print(s)
# print(s.str.upper())

# 合并 Series 和 dataframe 对象组合在一起。并使用各种索引集逻辑和关系代数功能来进行连接/合并类型的操作
# df = pd.DataFrame(np.random.randn(10, 4))
# print(df)
#
# print(df[:3])
#
# print(df[3:7])
#
# print(df[7:])

# 注意 dataframe 添加行速度相对较快。最好方式构造 dataframe
# print(df)
# pieces = [df[:3], df[3:7], df[7:]]
# print(pieces)

# print(pd.concat(pieces))

# 分组
# df = pd.DataFrame(
#     {
#         'A': ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"],
#         'B': ["one", "one", "two", "three", "two", "two", "one", "three"],
#         'C': np.random.randn(8),
#         'D': np.random.randn(8)
#     }
# )

# print(df)
# 根据 A 列分组 分组结果 后按照 c d 聚合
# print(df.groupby('A')[['C', 'D']].sum())
# 多列分组 首先根据 A 分组后根据B分组 组内聚合
# print(df.groupby(['A', 'B']).sum())

# 根据天分组 分组后结果聚合后最大减去最小值
df2 = pd.DataFrame(
    {
        'ts': pd.date_range('20240101', periods=8, freq='6h'),
        'v': np.random.randn(8)
    }
)
print(df2)
df3 = df2.groupby(df2['ts'].dt.date).agg(lambda x: x.max() - x.min())
print(df3)
